# app/parser/spiders/university_spider.py

import scrapy
import execjs  # 导入execjs库
import json
from app.parser.items import UniversityItem


class UniversitySpider(scrapy.Spider):
    """
    负责抓取上海软科高校列表的核心爬虫。
    本爬虫下载并执行payload.js文件来获取数据。
    """
    # 爬虫的唯一标识名
    name = 'university'

    # 将请求头定义为类属性，便于管理
    custom_headers = {
        "Accept": "application/json, text/plain, */*",
        "Content-Type": "application/json",
        "Referer": "https://www.shanghairanking.cn/institution",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
    }

    def start_requests(self):
        """
        Scrapy启动时调用的方法。我们在这里构建并发送一个POST请求。
        """
        # 目标JS文件的URL
        # 注意：URL中的数字(1758793770)是一个版本号或时间戳，如果未来爬虫失效，首先应检查这个URL是否已改变
        js_url = 'https://www.shanghairanking.cn/_nuxt/static/1758793770/institution/payload.js'

        # POST请求需要发送的数据体 (Payload)
        payload = {
            "type": "inst"
        }

        self.log(f"准备请求静态JS数据文件: {js_url}")

        # 使用 scrapy.Request 发送POST请求
        # - body需要是json格式的字符串
        # - callback指定了当请求成功后，由哪个方法来处理响应
        # 【注意】我们在这里使用的是GET请求，因为经过最终确认，该payload.js是一个静态文件，用GET即可获取。
        # 如果未来遇到必须POST的场景，只需将下一行的 `yield scrapy.Request` 改为我们之前讨论的POST版本。
        yield scrapy.Request(
            url=js_url,
            headers=self.custom_headers,
            callback=self.parse_js_payload
        )

    def parse_js_payload(self, response):
        """
        这个方法负责处理下载回来的payload.js文件的内容。
        """
        self.log(f"成功获取JS文件，准备进行改造和执行...")

        # 1. 获取JS文件内容
        js_code = response.text

        # 2. 改造JavaScript字符串，将其变为一个可执行的标准函数
        script = js_code.replace('__NUXT_JSONP__("/institution",', 'function getData(){ return ')
        script = script.replace(')));', '))};')

        try:
            # 3. 编译并执行JS函数，获取返回的数据
            ctx = execjs.compile(script)
            res = ctx.call('getData')

            # 4. 提取核心的大学数据列表
            univ_data_list = res.get('data')[0].get('univList')

        except Exception as e:
            self.log(f"执行JavaScript或提取数据失败: {e}")
            return

        self.log(f"成功从JavaScript中提取到 {len(univ_data_list)} 所大学的数据。")

        # 5. 遍历数据列表，填充并“产出”Item给Pipeline
        for university_data in univ_data_list:
            item = UniversityItem()

            # 从JSON字典中提取数据，并填充到Item的对应“隔间”
            item['name'] = university_data.get('nameCn')
            item['location'] = university_data.get('provinceShort')
            item['administration'] = university_data.get('adminType')
            item['category'] = university_data.get('categoryName')

            tags = university_data.get('tags')
            item['tags'] = tags if tags is not None else []  # 确保tags是一个列表

            univ_code = university_data.get('univCode')
            item['detail_url'] = f"https://www.shanghairanking.cn/institution/{univ_code}" if univ_code else None
            # 填充原材料，用于Pipeline计算哈希和归档
            item['raw_content'] = json.dumps(university_data, ensure_ascii=False)

            # 将打包好的“货箱”送上传送带
            yield item
